#define INTERNAL_NS_MALLOC
#include "NewSyndrome.h"

#if defined ENABLE_NS_MALLOC && defined NS_MALLOC_PROD

void *ns_calloc (const char *file, unsigned int line, size_t nmemb, size_t size);
void *ns_malloc (const char *file, unsigned int line, size_t size);
void ns_free (const char *file, unsigned int line, void *ptr);
void *ns_realloc (const char *file, unsigned int line, void *ptr, size_t size);
char *ns_strdup (const char *file, unsigned int line, const char *s);


struct ns_malloc_mod
{
	char *modname;
	module *ref;
	size_t mem;
};

static struct ns_malloc_mod *tab_mod = NULL;
static unsigned int nbmod = 0;
static unsigned int nbfree = 0, nballoc = 0, nberror = 0;


static int update_mod_data (size_t sub_size, size_t add_size)
{
	int i;
	
	if (ns_core.bot_state != MODULE && nbmod > 0)
	{
		tab_mod[0].mem += (add_size - sub_size);
		return (0);
	}
	
	
	if (ns_core.bot_state == MODULE)
	{
		for (i = 1; i < nbmod; i++)
			if (ns_core.current_module == tab_mod[i].ref)
			{
				tab_mod[i].mem += (add_size - sub_size);
				return (1);
			}
		
		for (i = 1; i < nbmod; i++)
			if (!strcmp (ns_core.current_module->name, tab_mod[i].modname))
			{
				tab_mod[i].mem += (add_size - sub_size);
				tab_mod[i].ref = ns_core.current_module;
				return (2);
			}
	}
	
		
	tab_mod = (struct ns_malloc_mod *) realloc (tab_mod, sizeof (struct ns_malloc_mod) * (nbmod + 1));
	if (ns_core.bot_state != MODULE)
	{
		tab_mod[nbmod].modname = strdup ("kernel");
		tab_mod[nbmod].ref = NULL;
	}
	else
	{
		tab_mod[nbmod].modname = strdup (ns_core.current_module->name);
		tab_mod[nbmod].ref = ns_core.current_module;
	}
	tab_mod[nbmod].mem = (add_size - sub_size);
	
	nbmod++;
	
	return (3);
}


void *ns_malloc (const char *file, unsigned int line, size_t size)
{
	size_t *ptr;
	
	update_mod_data (0, size);

	nballoc++;	
	ptr = malloc (size + sizeof (size_t));
	*ptr = size;
	return ((void *)(++ptr));
}


void *ns_calloc (const char *file, unsigned int line, size_t nmemb, size_t size)
{
	size_t *ptr;
	
	update_mod_data (0, size * (nmemb + sizeof (size_t)));
	
	nballoc++;
	ptr = calloc (nmemb + sizeof (size_t), size);
	*ptr = size * (nmemb + sizeof (size_t));
	return ((void *)(++ptr));
}


void *ns_realloc (const char *file, unsigned int line, void *ptr, size_t size)
{	
	size_t *new_ptr;
	
	new_ptr = (size_t *) ptr;
	
	if (new_ptr)
	{
		new_ptr--;
		update_mod_data (*new_ptr, size);
	}
	else
	{
		nballoc++;
		update_mod_data (0, size);
	}
	
	new_ptr = (size_t *) realloc (new_ptr, size + sizeof (size_t));
	*new_ptr = size;
	
	return ((void *)(++new_ptr));
}


void ns_free (const char *file, unsigned int line, void *ptr)
{
	size_t *new_ptr;

	new_ptr = (size_t *) ptr;
	if (new_ptr)
	{
		new_ptr--;
		nbfree++;
		update_mod_data (*new_ptr, 0);
		free (new_ptr);
	}
	else
	{
		nberror++;
		printf ("!!! NS_MALLOC : %s:%u free NULL ptr !!!\n", file, line);
	}
}



char *ns_strdup (const char *file, unsigned int line, const char *s)
{
	size_t *ptr, tmp;
	
	tmp = strlen (s) + 1;
	update_mod_data (0, tmp);
	
	nballoc++;
	ptr = (size_t *) malloc (tmp + sizeof (size_t));
	
	*ptr = tmp;
	ptr++;
	strcpy ((char *) ptr, s);

	return ((char *) ptr);
}


void ns_malloc_show_leak (void)
{
	int i;
	int total = 0;
	
	puts ("\n----------------------- NS MALLOC -----------------------");
	if (nberror)
		printf ("Total free error : %u errors\n\n", nberror);
	else
		printf ("Aucune erreur\n\n");
	
	for (i = 0; i < nbmod; i++)
		if (tab_mod[i].mem)
			total += tab_mod[i].mem;

	if (!total)
		printf ("Pas de memory leak\n");
	else
		printf ("Total leak : %d octets\n", total);
	
	printf ("\n%u allocs / %u frees\n", nballoc, nbfree);
	puts ("---------------------------------------------------------\n");
		
}


void ns_malloc_close (void)
{
	int i;
	for (i = 0; i < nbmod; i++)
		free (tab_mod[i].modname);

	free (tab_mod);
	tab_mod = NULL;
	nbmod = 0;
	nberror = 0;
	nbfree = 0;
	nballoc = 0;
}

#endif
